package com.gonsin.gview.logic.services;

import cn.hutool.core.collection.ConcurrentHashSet;
import com.gonsin.gview.logic.websocket.GViewCmd;
import com.gonsin.gview.logic.websocket.message.GViewMessage;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.websocket.Session;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Component
@Slf4j
public class GViewClientServiceImpl {

    // 在线终端
    private Set<Session> onlineSessions = new ConcurrentHashSet<>();

    // key = dataId， value = session列表
    private Map<String, Set<Session>> dataIdSessionMap = new ConcurrentHashMap<>();

    // key = session.getId， value = dataId列表
    private Map<String, Set<String>> sessionDataIdMap = new ConcurrentHashMap<>();

    private ReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * 响应数据变更
     */
    public void notifyDataChanged(String edificeKey, String dataId){
        lock.readLock().lock();
        try {
            String dataIdAndEdificeKey = dataId + "-" + edificeKey;
            Set<Session> sessions = dataIdSessionMap.get(dataIdAndEdificeKey);
            if(sessions == null){
                return;
            }
            for(Session session : sessions){

                GViewMessage message = GViewMessage.create()
                        .cmd(GViewCmd.Cmd.DATA)
                        .subCmd(GViewCmd.SubCmd.UPDATE)
                        .addArgs("dataId", dataId)
                        .addArgs("edificeKey", edificeKey);


                try {
                    session.getBasicRemote().sendText(new Gson().toJson(message));
                } catch (Exception e) {
                    log.error(e.toString(), e);
                    // 发送错误就断开连接
                    try {
                        session.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }
        } finally {
            lock.readLock().unlock();
        }
    }


    /**
     * 客户端上线
     */
    public void channelActive(Session session){
        lock.writeLock().lock();
        try {
            onlineSessions.add(session);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 客户端下线
     */
    public void channelInactive(Session session){
        lock.writeLock().lock();
        try {
            onlineSessions.remove(session);

            // 移除
            Set<String> dataIdAndEdificeKeys = sessionDataIdMap.get(session.getId());
            if(!CollectionUtils.isEmpty(dataIdAndEdificeKeys)){
                for(String dataIdAndEdificeKey : dataIdAndEdificeKeys){
                    Set<Session> sessions = dataIdSessionMap.computeIfAbsent(dataIdAndEdificeKey, k -> new ConcurrentHashSet<>());
                    sessions.remove(session);
                }
                dataIdAndEdificeKeys.clear();
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 添加到指定的数据id监听中
     */
    private void addToDataId(String dataIdAndEdificeKey, Session session){
        lock.writeLock().lock();
        try {
            Set<Session> sessions = dataIdSessionMap.computeIfAbsent(dataIdAndEdificeKey, k -> new ConcurrentHashSet<>());
            sessions.add(session);

            Set<String> dataIdAndEdificeKeys = sessionDataIdMap.computeIfAbsent(session.getId(), k -> new ConcurrentHashSet<>());
            dataIdAndEdificeKeys.add(dataIdAndEdificeKey);
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 指定的数据id监听中删除
     */
    private void removeDataId(String dataId, Session session){
        lock.writeLock().lock();
        try {
            Set<Session> sessions = dataIdSessionMap.computeIfAbsent(dataId, k -> new ConcurrentHashSet<>());
            sessions.remove(session);

            Set<String> dataIdAndEdificeKeys = sessionDataIdMap.computeIfAbsent(session.getId(), k -> new ConcurrentHashSet<>());
            dataIdAndEdificeKeys.remove(dataId);
        } finally {
            lock.writeLock().unlock();
        }
    }


    /**
     * 接收到客户端消息
     */
    public void channelRead(Session session, GViewMessage message) {
        try{
            if(GViewCmd.Cmd.GVIEW_CLIENT.equals(message.getCmd().getCmd())){

                // 心跳，不做任何处理
                if(GViewCmd.SubCmd.HEART.equals(message.getCmd().getSubcmd())){
                    return;
                }

                // 注册指令
                if(GViewCmd.SubCmd.REGISTER.equals(message.getCmd().getSubcmd())){
                    String dataId = (String) message.getArgs().get("dataId");
                    String edificeKey = (String) message.getArgs().get("edificeKey");
                    if(StringUtils.isNotEmpty(dataId)){
                        addToDataId(dataId + "-" + edificeKey, session);
                    }
                    return;
                }

                // 注销指令
                if(GViewCmd.SubCmd.UNREGISTER.equals(message.getCmd().getSubcmd())){
                    String dataId = (String) message.getArgs().get("dataId");
                    String edificeKey = (String) message.getArgs().get("edificeKey");
                    if(StringUtils.isNotEmpty(dataId)){
                        removeDataId(dataId + "-" + edificeKey, session);
                    }
                    return;
                }
            }

        } catch(Exception ex){
            log.error(ex.toString(), ex);
        }
    }
}
